package bosVM2.Loader;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Stack;


import bosVM2.Engine.ExecutionEngine;
import bosVM2.meta.LookUp;
import bosVM2.meta.LookUp.Type;
import bosVM2.meta.LookUp.opCode;
import bosVM2.meta.Objects.BosClass;
import bosVM2.meta.Objects.BosField;
import bosVM2.meta.Objects.BosMethod;

//BosVM2 translater, should be more sane.
public class Translater {	
	private Stack<File> work = new Stack<File>();
	ExecutionEngine e;
	public Translater(ExecutionEngine e)
	{
		this.e = e;
	}
	public HashMap<String,HashMap<Integer,Object[]>> Translate(File f) throws Exception
	{	
		HashMap<String, HashMap<Integer,Object[]>> mm = new HashMap<String,HashMap<Integer,Object[]>>();
		work.push(f);
		while(!(work.isEmpty())) {
			
			String currentClass = null;
			String currentMethod = null;
			HashMap<Integer,Object[]> ins = null;
			BufferedReader breader = new BufferedReader(new FileReader(work.pop()));
			
			while(breader.ready())
			{
				String[] line = breader.readLine().trim().split("\\s*(\\,|\\(|\\)|\\s+)\\s*");
				switch(line[0])
				{
					case "class":  currentClass = MakeClass(line); break;
					case "method": if(!(currentMethod == null)) 
								   { 
										e.getMeta().getMetaClass(currentClass).getMethod(currentMethod).addInstructions(ins); 
								   }
						           currentMethod = MakeMethod(line,currentClass);
						           ins = new HashMap<Integer,Object[]>(); break;
					case "field":  MakeField(line,currentClass);break;
					case "": break;
					default: ins.put(ins.size(),TranslateInstruction(line));
				}
				
			}
			e.getMeta().getMetaClass(currentClass).getMethod(currentMethod).addInstructions(ins);	
			breader.close(); 
		}
		return mm;	
	}

	private String MakeClass(String[] line)
	{  	
		if(!(e.getMeta().classKnown(line[1])))
		{
			if(line.length < 3)
			 { e.getMeta().addMetaClass(new BosClass(line[1],null));}
			else { e.getMeta().addMetaClass(new BosClass(line[1], e.getMeta().getMetaClass(line[2]))); }
			//TODO Extendable classes, try this, and we will crash
		}
		return line[1];
	}
	
	private String MakeMethod(String[] line, String clss)
	{
		BosClass cc = e.getMeta().getMetaClass(clss);
		String mName = line[2];
		Type t = LookUp.Type(line[1]);
		int numargs = (line.length-3)/2;
		String[] args = new String[numargs];
		for(int j = 0; j < numargs; j++) 
		{
			args[j] = line[(3+(2*j))+1];
		}
		e.getMeta().getMetaClass(clss).putMethod(new BosMethod(cc,mName,t,numargs,args));
		return line[2];
	}
	
	private void MakeField(String[] line, String clss) 
	{
		BosClass cc = e.getMeta().getMetaClass(clss);
		String fName = line[2];
		Type t = LookUp.Type(line[1]);
		e.getMeta().getMetaClass(clss).putField(new BosField(cc,fName,t));
	}
	
	private Object[] TranslateInstruction(String[] line) throws Exception {
		opCode code = LookUp.opCode(line[0]);
		switch(code)
		{
		//Core
		case NOP:	return new Object[]{code.getValue()}; 
		case PUSH: 	return new Object[]{code.getValue(), LookUp.Type(line[1]).getValue(), LookUp.getTypedValue(LookUp.Type(line[1]),line[2])};
		case POP:  	return transPop(code,line);
		case LOAD: 	return new Object[]{code.getValue(), LookUp.Type(line[1]).getValue(), line[2]};
		case STORE: return new Object[]{code.getValue(), LookUp.Type(line[1]).getValue(), line[2]};
		case ARIT: 	return new Object[]{code.getValue(), LookUp.aritOp(line[1]).getValue()};
		case IF: 	return new Object[]{code.getValue(), LookUp.ifOp(line[1]).getValue(), Integer.parseInt(line[3])};
		case GOTO:	return new Object[]{code.getValue(), Integer.parseInt(line[1])};
		 
		//Methods
		case INVOKEVIRTUAL: return new Object[]{code.getValue(), line[1]};
		case RETURN: if(line.length == 1) { return new Object[] {code.getValue()}; }
				     else { return new Object[]{code.getValue(),LookUp.Type(line[1]).getValue()}; }
		//Objects
		case NEW: 	
			pushFile(line[1]);
			return new Object[]{code.getValue(), line[1]};
		case PUTFIELD:return new Object[]{code.getValue(), line[1]};
		case GETFIELD:return new Object[]{code.getValue(), line[1]};
		case THROW: return new Object[]{code.getValue(), line[1]};//Not actually implemented yet
		default: System.err.println("Unknown Instruction");
				 throw new Exception();
		
		}
	}

	private Object[] transPop(opCode code,String[] line)
	{
		if(line.length > 1) {
			return new Object[]{code.getValue(), Integer.parseInt(line[1])};
		} else {
			return new Object[]{code.getValue(), 1};
		}
	}
	private void pushFile(String file) {
		file = file.replace(".", "/");
		file = file + ".tbc";
		work.push(new File(file));
	}
}
